﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using YK.Framework.Repository;

namespace YK.Framework.Security
{
    public class RoleService
    {
        IRepository repository;

        public RoleService(IRepository repository)
        {
            this.repository = repository;
        }


        public int CreateRole(string applicationName, string roleName)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            if (RoleExists(applicationName, roleName))
            {
                return 5;
            }

            Role role = new Role();
            role.Name = roleName;
            role.Application = application;

            repository.Save<Role>(role);

            return 0;
        }


        public int DeleteRole(string applicationName, string roleName, bool deleteOnlyIfRoleIsEmpty)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            Role role =
                repository.Query<Role>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Name == roleName)
                    .SingleOrDefault();

            if (role == null)
            {
                return 2;
            }

            if (deleteOnlyIfRoleIsEmpty)
            {
                if (role.Users.Count<User>() > 0)
                {
                    return 4;
                }
            }

            repository.Delete<Role>(role);

            return 0;
        }

        public bool RoleExists(string applicationName, string roleName)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            Role role =
                repository.Query<Role>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Name == roleName)
                    .SingleOrDefault();

            if (role != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        public string[] FindUsersInRole(string applicationName, string roleName, string usernameToMatch)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            Role role =
                repository.Query<Role>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Name == roleName)
                    .SingleOrDefault();

            if (role == null)
            {
                return null;
            }

            IList<User> userList = new List<User>();

            foreach (var user in role.Users)
            {
                if (user.Username.IndexOf(usernameToMatch) >= 0)
                {
                    userList.Add(user);
                }
            }

            if (userList.Count == 0)
            {
                return null;
            }

            string[] usernames = new String[userList.Count];

            int i = 0;
            foreach (User user in userList)
            {
                usernames[i++] = user.Username;
            }

            return usernames;
        }

        public string[] GetAllRoles(string applicationName)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            IList<Role> roles =
               repository.Query<Role>()
                   .Where(x => x.Application == application)
                   .ToList();

            if (roles.Count == 0)
            {
                return null;
            }

            string[] roleNames = new String[roles.Count];

            int i = 0;
            foreach (var role in roles)
            {
                roleNames[i++] = role.Name;
            }

            return roleNames;
        }

        public string[] GetRolesForUser(string applicationName, string username)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            User user =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username == username)
                    .SingleOrDefault();

            if (user == null)
            {
                return null;
            }

            string[] roleNames = new String[user.Roles.Count<Role>()];

            int i = 0;
            foreach (Role role in user.Roles)
            {
                roleNames[i++] = role.Name;
            }

            return roleNames;
        }

        public string[] GetUsersInRole(string applicationName, string roleName)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            Role role =
                repository.Query<Role>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Name == roleName)
                    .SingleOrDefault();

            if (role == null)
            {
                return null;
            }

            if (role.Users.Count<User>() == 0)
            {
                return null;
            }

            string[] usernames = new String[role.Users.Count<User>()];

            int i = 0;
            foreach (var user in role.Users)
            {
                usernames[i++] = user.Username;
            }

            return usernames;
        }

        public bool IsUserInRole(string applicationName, string username, string roleName)
        {
            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            User user =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username == username)
                    .SingleOrDefault();

            if (user == null)
            {
                return false;
            }

            Role role =
                repository.Query<Role>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Name == roleName)
                    .SingleOrDefault();

            if (role == null)
            {
                return false;
            }

            if (user.Roles.Contains(role))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public int AddUsersToRoles(string applicationName, string[] usernames, string[] roleNames)
        {
            Application application =
                 new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users = new List<User>();
            IList<Role> roles = new List<Role>();

            foreach (string username in usernames)
            {
                User user =
                    repository.Query<User>()
                        .Where(x => x.Application == application)
                        .Where(x => x.Username == username)
                        .SingleOrDefault();

                if (user == null)
                {
                    return 1;
                }

                users.Add(user);
            }


            foreach (string roleName in roleNames)
            {
                Role role =
                    repository.Query<Role>()
                        .Where(x => x.Application == application)
                        .Where(x => x.Name == roleName)
                        .SingleOrDefault();

                if (role == null)
                {
                    return 2;
                }

                roles.Add(role);
            }


            foreach (User user in users)
            {
                foreach (Role role in roles)
                {
                    if (!user.Roles.Contains(role))
                    {
                        ((IList<Role>)user.Roles).Add(role);

                        repository.Save<User>(user);
                    }
                }
            }

            return 0;
        }


        public int RemoveUsersFromRoles(string applicationName, string[] usernames, string[] roleNames)
        {
            Application application =
                 new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users = new List<User>();
            IList<Role> roles = new List<Role>();

            foreach (string username in usernames)
            {
                User user =
                    repository.Query<User>()
                        .Where(x => x.Application == application)
                        .Where(x => x.Username == username)
                        .SingleOrDefault();

                if (user == null)
                {
                    return 1;
                }

                users.Add(user);
            }


            foreach (string roleName in roleNames)
            {
                Role role =
                    repository.Query<Role>()
                        .Where(x => x.Application == application)
                        .Where(x => x.Name == roleName)
                        .SingleOrDefault();

                if (role == null)
                {
                    return 2;
                }

                roles.Add(role);
            }


            foreach (User user in users)
            {
                foreach (Role role in roles)
                {
                    if (user.Roles.Contains(role))
                    {
                        ((IList<Role>)user.Roles).Remove(role);

                        repository.Update<User>(user);
                    }
                }
            }

            return 0;
        }

        public Role GetRole(int roleID)
        {
            return repository.Get<Role>(roleID);
        }

    }
}